package xin.nick.summer.servlet;

import xin.nick.summer.annotation.Autowired;
import xin.nick.summer.annotation.Controller;
import xin.nick.summer.annotation.RequestMapping;
import xin.nick.summer.annotation.Service;
import xin.nick.summer.interceptor.SummerInterceptor;
import xin.nick.summer.pojo.Handler;
import xin.nick.summer.util.MyUtil;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Nick
 * @date 2021/5/29
 * @description
 */
public class DispatcherServlet extends HttpServlet {

    /**
     * 配置对象
     */
    private Properties properties = new Properties();

    /**
     * 扫描到的类全限定类名
     */
    private List<String> classNameList = new ArrayList<>();

    /**
     * ioc容器
     */
    private Map<String, Object> ioc = new HashMap<>();

    /**
     * handlerMapping 请求映射器
     */
    private List<Handler> handlerList = new ArrayList<>();
    private Map<String, Handler> handlerMapping = new HashMap<>();

    /**
     * 拦截器列表
     */
    private List<SummerInterceptor> interceptList = new ArrayList<>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");

        // 根据url,找到method方法,调用
        String requestUri = req.getRequestURI();

        Handler handler = getHandler(requestUri);

        if(handler == null) {
            resp.getWriter().write("没有找到哦");
            return;
        }

        // 准备参数
        Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
        Object[] args = new Object[parameterTypes.length];
        // 传过来的参数
        Map<String, String[]> parameterMap = req.getParameterMap();

        // 遍历参数,设置 args
        for (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
            String value = String.join(",", param.getValue());
            // 填充数据
            if(!handler.getParamIndexMapping().containsKey(param.getKey())) {
                continue;
            }

            // 找到参数位置,加进去
            Integer index = handler.getParamIndexMapping().get(param.getKey());
            args[index] = value;
        }

        // request 和 response 需要额外加一下
        Integer requestInt = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName());
        if (requestInt != null) {
            args[requestInt] = req;
        }

        Integer responseInt = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName());
        if (requestInt != null) {
            args[responseInt] = resp;
        }

        try {
            // 前置拦截器
            for (SummerInterceptor summerInterceptor : interceptList) {
                if(!summerInterceptor.preHandle(req, resp, handler)) {
                    return;
                }
            }
            Object result = handler.getMethod().invoke(handler.getController(), args);
            resp.getWriter().write(result.toString());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取到Handler
     * @param url
     * @return
     */
    private Handler getHandler(String url) {
        if(handlerMapping.get(url) != null) {
            return handlerMapping.get(url);
        }
        if(handlerList.isEmpty()) {
            return null;
        }
        for (Handler handler : handlerList) {
            Matcher matcher = handler.getPattern().matcher(url);
            if (!matcher.matches()) {
                continue;
            }
            handlerMapping.put(url, handler);
            return handler;
        }
        return null;
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        // 初始化加载配置文件
        String contextConfigLocation = config.getInitParameter("contextConfigLocation");
        doLoadConfig(contextConfigLocation);

        // 包扫描,扫描注解,
        doScan(properties.getProperty("scanPackage"));
        
        // 初始化bean对象(实现ioc容器)
        doInstance();

        // 实现依赖注入
        doAutoWired();

        // 构造 HandlerMapping 处理器映射器,将配置好的url和method简历映射关系
        initHandlerMapping();

        // 获取拦截器列表
        handleInterceptor();

        // 处理完成 等待请求进入
        System.out.println("初始化完成");
    }

    /**
     * 处理过滤器
     */
    private void handleInterceptor() {
        if(ioc.isEmpty()) {
            return;
        }

        for (Map.Entry<String, Object> entry : ioc.entrySet()) {

            Class<?>[] interfaces = entry.getValue().getClass().getInterfaces();
            for (Class<?> anInterface : interfaces) {
                if(anInterface == SummerInterceptor.class) {
                    interceptList.add((SummerInterceptor) entry.getValue());
                }
            }
        }
    }

    /**
     * 初始化处理器映射器
     */
    private void initHandlerMapping() {
        if(ioc.isEmpty()) {
            return;
        }

        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();

            // 非 Controller 排除
            if(!clazz.isAnnotationPresent(Controller.class)) {
                continue;
            }

            String baseUrl = "";
            if(clazz.isAnnotationPresent(RequestMapping.class)) {
                RequestMapping requestMapping =
                        clazz.getAnnotation(RequestMapping.class);
                baseUrl = requestMapping.value();
            }

            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                if (!method.isAnnotationPresent(RequestMapping.class)) {
                    continue;
                }
                RequestMapping requestMapping =
                        method.getAnnotation(RequestMapping.class);
                String methodName = requestMapping.value();
                String url = new StringBuilder(baseUrl).append(methodName).toString();
                // 创建处理器
                Handler handler = new Handler(entry.getValue(), method, Pattern.compile(url));
                //                handlerMapping.put(url, method);

                // 计算方法参数和位置信息
                Parameter[] parameters = method.getParameters();
                int len = parameters.length;
                for (int j = 0; j < len; j++) {
                    Parameter parameter = parameters[j];
                    if (parameter.getType() == HttpServletRequest.class
                        || parameter.getType() == HttpServletResponse.class) {
                        handler.getParamIndexMapping().put(parameter.getType().getSimpleName(), j);
                    } else {
                        handler.getParamIndexMapping().put(parameter.getName(), j);
                    }
                }

                // 添加到list
                handlerList.add(handler);
            }

        }
    }

    /**
     * 自动注入
     */
    private void doAutoWired() {

        if(ioc.isEmpty()) {
            return;
        }

        // 遍历ioc容器,
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {

            // 遍历对象的属性,有AutoWired注解的需要进行注入
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {
                if (!field.isAnnotationPresent(Autowired.class)) {
                    continue;
                }

                // 有注解则进行注入
                Autowired annotation = field.getAnnotation(Autowired.class);
                String beanName = annotation.value();
                // 注解有value则进行名字注入,没有value则进行类型注入
                if ("".equals(beanName)){
                    beanName = field.getType().getName();
                }


                field.setAccessible(true);

                try {
                    field.set(entry.getValue(), ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }


    }

    /**
     * 实例化对象
     */
    private void doInstance() {
        // 基于className缓存类的全限定类名,利用反射技术,完成对象创建
        if(classNameList.size() == 0) {
            return;
        }

        try {
            for (String className : classNameList) {
                Class<?> clazz = Class.forName(className);

                // 加载所有的组件
                if (MyUtil.hasComponentAnnotation(clazz)) {

                    // Service 注解的,需要注意下接口
                    if(clazz.isAnnotationPresent(Service.class)) {
                        Service serviceAnnotation = clazz.getAnnotation(Service.class);
                        String beanName = serviceAnnotation.value();

                        if (!"".equals(beanName.trim())) {
                            ioc.put(beanName, clazz.newInstance());
                        } else {
                            beanName = MyUtil.lowerFist(clazz.getSimpleName());
                            ioc.put(beanName, clazz.newInstance());
                        }

                        // 根据类型名,将实例放到ioc
                        Class<?>[] interfaces = clazz.getInterfaces();
                        for (Class<?> anInterface : interfaces) {
                            ioc.put(anInterface.getName(), clazz.newInstance());
                        }
                    } else {
                        String simpleName = clazz.getSimpleName();
                        String lowerFirstSimpleName = MyUtil.lowerFist(simpleName);
                        Object o = clazz.newInstance();
                        ioc.put(lowerFirstSimpleName, o);
                    }
                }
            }
        } catch(Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 扫描类
     * @param scanPackage
     */
    private void doScan(String scanPackage) {
        String parentPath = Thread.currentThread().getContextClassLoader().getResource("").getPath();
        String scanPackagePath = parentPath + scanPackage.replace(".", File.separator);
        File pack = new File(scanPackagePath);

        File[] files = pack.listFiles();
        for (File file : files) {
            if(file.isDirectory()) {
                doScan(new StringBuilder(scanPackage).append(".").append(file.getName()).toString());
            } else if (file.getName().endsWith(".class")) {
                String className = new StringBuilder(scanPackage).append(".").append(file.getName().replaceAll(".class", "")).toString();
                classNameList.add(className);
            }
        }
    }

    /**
     * 载入配置文件
     * @param contextConfigLocation
     */
    private void doLoadConfig(String contextConfigLocation) {
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            properties.load(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
